package com.itmuch.box.core.security;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import com.itmuch.box.util.PasswordEncoderUtil;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    private DataSource dataSource;

    @Value("${adminPath}")
    private String adminPath;

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 设置不拦截规则
        web.ignoring().antMatchers("/static/**");

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 下面这一行代码, 用于禁用掉符合/xxx/** 规则的url的csrf防御, 做个备忘.
        // http.csrf().ignoringAntMatchers("/xxx/**").and()

        // X-Frame-Options, 防止跨域的设置. 默认是DENY, 这边设置成允许同域名下访问. 如果不设置这个, ueditor上传图片什么的就不正常了.
        http.headers().frameOptions().sameOrigin().and()

                // 允许所有用户访问”/”和”/home”
                .authorizeRequests().antMatchers("/**").permitAll()
                // 其他地址的访问均需验证权限
                .anyRequest().authenticated().and()

                // 指定登录页是/login登录成功后可使用loginSuccessHandler()存储用户信息，可选。
                .formLogin().loginPage("/login").successHandler(this.loginSuccessHandler("/"))
                // 登陆失败页面
                .failureHandler(this.loginFailureHandler("/login?error")).permitAll().and()

                // 退出
                .logout().logoutUrl("/logout").logoutSuccessUrl("/").permitAll().invalidateHttpSession(true).and()

                // 登录后记住用户，7天内自动登陆
                // 数据库中必须存在名为persistent_logins的表
                .rememberMe().userDetailsService(this.customUserDetailsService).tokenValiditySeconds(604800)
                // 指定记住登录信息所使用的数据源
                .tokenRepository(this.tokenRepository());

    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.authenticationProvider(this.authenticationProvider());
        // 不删除凭据，以便记住用户
        auth.eraseCredentials(false);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return PasswordEncoderUtil.getEncoder();
    }

    // 用于remember-me 存取表 persistent_logins
    @Bean
    public JdbcTokenRepositoryImpl tokenRepository() {
        JdbcTokenRepositoryImpl j = new JdbcTokenRepositoryImpl();
        j.setDataSource(this.dataSource);
        return j;
    }

    @Bean
    public LoginSuccessHandler loginSuccessHandler(String defaultSuccessUrl) {
        LoginSuccessHandler handler = new LoginSuccessHandler();
        handler.setDefaultTargetUrl(defaultSuccessUrl);
        return handler;
    }

    @Bean
    public LoginFailHandler loginFailureHandler(String failureUrl) {
        LoginFailHandler handler = new LoginFailHandler();
        handler.setDefaultFailureUrl(failureUrl);
        return handler;
    }

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setHideUserNotFoundExceptions(false);
        provider.setUserDetailsService(this.customUserDetailsService);

        provider.setPasswordEncoder(this.passwordEncoder());
        return provider;
    }

}